package com.cfpl.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.cfpl.admin_service.VO.LoginAdmin;
import com.cfpl.config.RedisUtils;
import com.cfpl.project_common.constant.RedisConstants;
import com.cfpl.project_common.context.BaseContext;
import com.cfpl.project_common.enums.CodeEnum;
import com.cfpl.project_common.exception.BusException;
import com.cfpl.project_common.util.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: LYP
 * @Date: 2023/10/14 - 10:35
 * @Description: com.cfpl.admin_service.filter
 * @version: 1.0
 * 过滤器链认证token    未登录时可以让spring security的过滤器链来返回未登录结果
 */
@Component
@Slf4j
public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//        log.info("拦截到***{}***",request.getRequestURI());

        String token = request.getHeader("token");
        //未登录时可以让spring security的过滤器链来返回未登录结果
        if (StrUtil.isBlank(token)){
            filterChain.doFilter(request,response);
            return;
        }

        // 当解析失败自动抛出全局异常，这里不用做ID验证
        Long adminID = JWTUtil.getUserId(token);
        log.info("拦截用户ID:{}",adminID);


        String redisKey = RedisConstants.LOGIN_ADMIN +token;

//        过滤器或拦截器在生效时，比你的注入要更快，导致redisTemplate还没注入就使用所以会报空指针异常 https://blog.csdn.net/qq_48445961/article/details/116462520
        try {
            String adminJSON = RedisUtils.stringRedisTemplate.opsForValue().get(redisKey);
            LoginAdmin loginAdmin = JSONUtil.toBean(adminJSON, LoginAdmin.class);
            //如果说在这里查不到用户的话，说明用户已经注销退出登录了,token还能解析，但是Redis中已经没有用户信息
            if (Objects.isNull(loginAdmin.getAdmin())){
                throw new BusException(CodeEnum.USER_NOT_LOGIN);
            }
            //线程存储当前请求用户ID
            BaseContext.setCurrentUserId(loginAdmin.getAdmin().getAdminId());
            /**
             * 用三个参数的构造方法，在token认证中，如果执行到这一步说明用户已被认证,在之后的过滤器链中就不需要在对用户进行认证
             *  两个参数的认证状态为false
             */
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(loginAdmin,null,loginAdmin.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            //刷新token过期时间
            RedisUtils.stringRedisTemplate
                    .expire(RedisConstants.LOGIN_ADMIN+token,
                            RedisConstants.EXPIRE_TIME_SECONDS,
                            TimeUnit.SECONDS);
        }catch (Exception e){
            throw new BusException(CodeEnum.USER_NOT_LOGIN);
        }

        //放行
        filterChain.doFilter(request,response);

    }


}
